分支限界法

1、单源最短路径问题: 给定一个带权有向图G=(V,E),其中每条边的权是一个非负实数,V={1,2,...,n}。设顶点v作为源顶点。要计算从源到所有其他各顶点的最短路径长度。
  Dijkstra算法是解决这个问题的一个著名的贪心算法。这个问题也适合用分支限界法来求解。分支限界法类似于回溯法,也是在问题的解空间树(对这里的问题则为图)上搜索问题的解。但分支限界法采用广度优先或最小耗费优先的方式来搜索,即遍历到一个结点之后,接着就遍历其相邻的所有结点。这就意味着需要一个队列结构来完成树或图的遍历。初始时根结点入队,按结点的出队顺序进行遍历,遍历到一个结点时,就让其相邻的所有结点入队。搜索过程直到队列变空为止。对树来说,广度优先搜索就相当于层次遍历。常用的队列有先进先出(FIFO)队列、优先级队列。与一般的广度优先遍历不同,分支限界法通常可以使用限界函数(即剪枝函数)来计算结点的值,以剪去导致无效解或非最优解的子树,即让不可行的结点不入队,这样就不会搜索到以不可行结点为根的子树。由于使用队列结构来进行遍历,因此分支限界法通常不需要递归,这可以节省一些递归展开的时间开销。但它需要一个额外的队列结构,我们知道递归展开则使用了栈结构,可见与回溯法相比,分支限界法并不占太大的便宜。
  (1)定义问题的解空间:这里的解是源顶点到其他各顶点的最短路径。我们可以用数组prev来记录从源到各顶点的最短路径上前驱顶点,即prev[i]是从源到顶点i的最短路径上i的前一个顶点。这样prev[i]=i1, prev[i1]=i2, ..., prev[ik]=v(到达源顶点v),就可以找到从源顶点v到任意顶点的i的最短路径"1-->ik-->...-->i2-->i1-->i"。我们用数组dist记录源到各顶点最短路径的长度。初始时,dist[i]为源顶点v到顶点i的边上的权值,prev[i]为源顶点v,若没有直接相连的边,则(v,i)用一个充分大的值NoEdge表示,且prev[i]=0,表示无前驱。
  (2)确定解空间的结构:这里的解空间树以源顶点为根,包含源顶点到每一个顶点的所有可能的路径。这里可以直接对图进行遍历。图用邻接矩阵表示。
  (3)以广度优先方式搜索整个解空间,找出所要的解:这里是求从源到各顶点的最短路径,因此要使用最小堆表示的优先级队列来进行广度优先搜索。入队的顶点必须还要附带一个优先级域,队列根据这个域来判定结点的出队优先级。这里要使用从源到该顶点的最短路径长度作为优先级域。初始时源结点入队。对队列中所有活结点,每次总是选取从源到这些结点中路长最短的结点E出队,以作为当前扩展结点,然后检查与E相邻的所有结点,这时我们并不让它们都入队,而是通过限界函数来剪去一些结点。只有从顶点E到顶点j有边可达,且从源出发途经顶点E到j的路径长度小于当前最优路长(记录在dist[j]中),才会把这个顶点j插入到队列中,并设置j的前驱结点为E。不满足这个限界条件的相邻顶点都将被剪去。由于每次选取的途经结点E都是离源的路径长度最短,因此能保证最后得到的所有路径长度都是最短的。
  (4)分支限界法的数据结构描述:整个问题包括解空间树的结点信息,用于构造最优解的数据成员、可选的限界函数,分支限界法搜索函数BranchAndBound(i),算法实现函数等。通常用一个类来描述这些信息,算法实现函数也可以一个独立的全局函数。优先级队列中的结点需要附带优先级信息,用一个独立的类MinHeapNode来描述队列中的结点信息。对于单源最短路径问题,数据结构描述如下:

[cpp]  view plain   copy
  1. //问题及其解空间描述  
  2. template<class Type>  
  3. class ShortestPathsProblem{  
  4. public:  
  5.     friend void ShortestPaths(int,Type**,int*,Type*,int,Type); //算法实现函数  
  6.     void BranchAndBound(int);  //分支限界搜索函数  
  7. private:  
  8.     int n;  //图G的顶点数  
  9.     int* prev;  //前驱顶点数  
  10.     Type** c;  //图G的邻接矩阵  
  11.     Type* dist; //最短距离数组  
  12.     Type NoEdge;  //无边标记,一个充分大的值  
  13. };  
  14. //优先级队列的结点描述  
  15. template<class Type>  
  16. class MinHeapNode{  
  17. public:  
  18.     friend ShortestPathProblem<Type>;  
  19.     operator int() const//优先级队列通过本函数来判定结点的优先级,以进行出队操作  
  20.         return length;  
  21.     }  
  22. private:  
  23.     int i; //顶点  
  24.     Type length;  //从源到本顶点的最短路长作为优先级  
  25. };  

  限界函数:有边可达的条件为c[E.i][j]<NoEdge。从源出发途经顶点E到j的路径长度小于当前最优路长的条件为E.length+c[E.i][j]<dist[j],这两个条件同时满足时,结点j才会被插入到队列中,否则剪去结点j。这两个条件比较简单,可以在分支限界法的实现函数中直接使用,因此这里并没有设计成一个独立的函数。
  分支限界搜索函数:源结点为初始的扩展结点,然后使用优先级队列来进行广度优先遍历,在遍历过程中用限界函数剪去导致无效解或非最优解的子树。函数实现如下:

[cpp]  view plain  
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
分支限界(Branch and Bound)是一种用于求解组合优化问题的搜索算,特别适用于旅行商问题(Traveling Salesman Problem, TSP),这是一个经典的NP完全问题,目标是找到一条最短路径,使得一位旅行商能访问所有城市一次后返回起点。 在Python中,我们可以使用branch and bound方来解决TSP问题,通常会结合一些启发式算如贪心策略或遗传算来改善搜索效率。以下是一个简化版的分支限界TSP求解步骤: 1. **定义状态**:用一个列表表示当前城市的集合,以及一个游程表示当前路径。 2. **初始状态**:从所有城市中随机选择一个作为起始城市,其他城市按顺序加入游程。 3. **分支过程**:对于游程中的最后一个城市,生成所有可能的下一个城市,并形成新的状态。 4. **剪枝策略**:计算每个状态的上界值(通常是通过估算得到的),如果一个状态的上界大于已知最优解,那么就无需进一步探索该子树。 5. **回溯**:选择上界最小的节点进行扩展,直到所有可能的分支都被穷举或达到某个停止条件(例如达到最大深度或时间限制)。 6. **更新最优解**:在每次剪枝后,检查当前状态是否为最优解。 以下是一个简化的伪代码示例: ```python def tsp_brain_bound(states, start, visited): # 初始化 upper_bound = calculate_upper_bound(states) best_solution = None while states: # 选择上界最小的节点 next_state, min_upper_bound = choose_next(states, visited) if min_upper_bound < current_best: # 探索子节点 children = generate_children(next_state) for child in children: tsp_brain_bound(child, start, visited) else: # 剪枝 remove_states(states, next_state) # 返回最优解 return best_solution ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值